package com.cpic.nonauto.bloomfilter.lock;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * redis 实现分布式锁
 *
 * @author admire
 */
@Order(3)
@Aspect
@Component("RedisLockAspect")
public class RedisLockAspect {

    @Autowired
    private RedisLockHelper redisLockHelper;
    private Logger logger = LoggerFactory.getLogger(RedisLockAspect.class);

    @Around("@annotation(RedisLock)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().getName();
        Class<?> clazzTarget = joinPoint.getTarget().getClass();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Class<?>[] parameterTypes = signature.getParameterTypes();
        Method method = clazzTarget.getMethod(methodName, parameterTypes);
        Object[] args = joinPoint.getArgs();


        RedisLock redisLock = method.getAnnotation(RedisLock.class);

        final String keyPrefix = redisLock.keyPrefix();
        final String keyExpression = redisLock.keyExpression();

        String key = this.getKeyExpression(method, args, keyExpression);
        if (StringUtils.isEmpty(key)) {
            logger.error("分布式锁未获取到key，method：{}，keyExpression:{}", methodName, keyExpression);
            return joinPoint.proceed();
        }
        String lockKey = keyPrefix + key;
        String value = redisLockHelper.uniqueId();
        logger.info("开始分布式锁加锁key：{}，method：{}，keyExpression:{},value:{}", lockKey, methodName, keyExpression, value);
        boolean result = false;
        try {

            if (redisLock.isBlocking()) {
                /*阻塞锁*/
                result = redisLockHelper.lockWithWaitTime(lockKey, value, redisLock.expireTime(), redisLock.waitTime(), redisLock.retrySleepTime());
            } else {
                /*非阻塞锁*/
                result = redisLockHelper.lockWithLua(lockKey, value, redisLock.expireTime(), TimeUnit.MILLISECONDS);
            }
            if (!result) {
                logger.warn("分布式锁加锁失败key：{}，method：{}，keyExpression:{},value:{}", lockKey, methodName, keyExpression, value);
                throw new RuntimeException("获取锁失败，请稍后再试");
            }
            logger.info("分布式锁加锁成功key：{}，method：{}，keyExpression:{},value:{}", lockKey, methodName, keyExpression, value);
            return joinPoint.proceed();
        } finally {
            if (result) {
                //释放锁，当服务器发生崩溃时释放锁
                boolean unlockWithLua = redisLockHelper.unlockWithLua(lockKey, value);
                logger.info("分布式锁加锁结果:{}key：{}，method：{}，keyExpression:{},value:{}", unlockWithLua, lockKey, methodName, keyExpression, value);
            }

        }
    }

    /**
     * 根据
     *
     * @param method        Method
     * @param args          Object[]
     * @param keyExpression String
     * @return
     */
    private String getKeyExpression(Method method, Object[] args, String keyExpression) {
        if (StringUtils.isEmpty(keyExpression)) {
            throw new RuntimeException("未设置keyExpression");
        }
        return (String) SpelExecutor.getInstance().execute(keyExpression, method, args);
    }
}